/* Release 3.00 */

/*-------------------------------------------------------------------------*
*                                                                          *
*  Epoc1.c - Epoc/Os interface routines                                     *
*                                                                          *
*  COPYRIGHT (C) 1991 Psion PLC. All Rights Reserved                       *
*  Written by NSM.                                                         *
*                                                                          *
*--------------------------------------------------------------------------*/

#define _USE_CONIO
#define _USE_TIME
#include <clib.h>

#define SZMBLK sizeof(_MBLOCK) /* size of free cell header */
#define SZHD sizeof(unsigned int) /* size of allocated cell header */

extern char DatHeapLocked;

int _nheapwalk(struct _nheapinfo *pE)
/*
Walks through every cell in the heap (whether allocated
or free) from low to high address. Also checks the heap
for consistency and returns an error if something is wrong.
*/
    {
    int ret;
    _MBLOCK *p1,*p2;
    
    ret=_HEAPOK;
    DatHeapLocked++;
    if (pE->_pentry==NULL)
	{
	if (_allocInfo(&p1,&p2)==0)
	    {
	    ret=_HEAPEMPTY;
	    goto walkExit;
	    }
	goto advanceFreePtr;
	}
    else
	{
	if (pE->_useflag) /* Was allocated */
	    {
	    p1=(_MBLOCK *)(((char *)pE->_pentry)-SZHD);
	    p2=pE->_secret;
	    }
	else /* Was free */
	    {
	    p2=(_MBLOCK *)(((char *)pE->_pentry)-SZMBLK);
	    p1=pE->_secret;
	    }
	if (p1<p2) /* Allocated cell is before free cell */
            p1=(_MBLOCK *)(((char *)p1)+p1->mlen);
	else /* Free cell is before allocated cell */
	    {
advanceFreePtr:
	    p2=p2->mnext; /* Points to first free cell */
            if (p2!=NULL &&
		((((int)p2)&1) || (p2>_heapTop()-1) ||
		(p2->mnext && p2->mnext<p2+1)))
		{
		ret=_HEAPBADPTR;
		goto returnFree;
		}
	    }
	}
    if (p2 && p1<p2)
	{
	if ((((int)p1)&1) || p1->mlen<SZMBLK || (p1->mlen&1) ||
	    p1->mlen>((char *)p2-(char *)p1))
	    {
badNode:
	    ret=_HEAPBADNODE;
	    }
	pE->_pentry=(int *)(((char *)p1)+SZHD);
	pE->_size=p1->mlen-SZHD;
	pE->_secret=p2;
	pE->_useflag=1;
	}
    else if (p2)
	{
        p1=(_MBLOCK *)(((char *)p2)+p2->mlen); /* next allocated cell */
returnFree:
	pE->_pentry=(int *)(((char *)p2)+SZMBLK);
	pE->_size=p2->mlen-SZMBLK;
	pE->_secret=p1;
	pE->_useflag=0;
	}
    else if (_heapTop()!=p1)
	goto badNode;
    else
	ret=_HEAPEND;
walkExit:
    DatHeapLocked--;
    return(ret);
    }

static int heapDoIt(int flag,int fill)
/*
Actually does _nheapchk() and _nheapset().
*/
    {
    int ret;
    struct _nheapinfo hi;

    hi._pentry=NULL;
    while ((ret=_nheapwalk(&hi))==_HEAPOK)
	{
	if (flag && hi._useflag==0)
	    memset(hi._pentry,(char)fill,hi._size);
	}
    if (ret==_HEAPEND)
	ret=_HEAPOK;
    return(ret);
    }

int _nheapchk(void)
/*
Check the entire heap for consistency.
*/
    {

    return(heapDoIt(0,0));
    }

int _nheapset(int fill)
/*
Check the entire heap for consistency and fill all
free cells with fill as we go along.
*/
    {

    return(heapDoIt(1,fill));
    }

size_t _memmax(void)
/*
Returns the size of the biggest free cell.
*/
    {
    size_t ret,i;
    _MBLOCK *p1,*p2;
    
    ret=0;
    DatHeapLocked++;
    if (_allocInfo(&p1,&p2)==0)
	goto memExit;
    while ((p2=p2->mnext)!=NULL) /* Scan free cells */
	{
	i=p2->mlen-sizeof(unsigned int);
	if (i>ret)
	    ret=i;
	}
    i=(unsigned int)0xfe00-(unsigned int)_heapTop()-sizeof(unsigned int);
    if (i>ret)
	ret=i;
memExit:
    DatHeapLocked--;
    return(ret);
    }

static unsigned int memCalc(unsigned int num,unsigned int den)
/*
Works out how many times den will fit into num, where num is
the size of a free block in the heap.
*/
    {
    unsigned int ret;

    num-=sizeof(unsigned int);
    ret=num/den;
    if (ret && (num%den)<SZMBLK)
	ret--;
    return(ret);
    }

unsigned int _freect(size_t _size)
/*
Returns the number of times _size can be allocated from
the heap.
*/
    {
    unsigned int ret;
    _MBLOCK *p1,*p2;

    ret=0;
    if (_size&1)
	_size+=1;
    if (_size<SZMBLK)
	_size+=2;
    DatHeapLocked++;
    if (_allocInfo(&p1,&p2)==0)
	goto freeExit;
    while ((p2=p2->mnext)!=NULL) /* Scan free cells */
	ret+=memCalc(p2->mlen,_size);
    ret+=memCalc((unsigned int)0xfe00-(unsigned int)_heapTop(),_size);
freeExit:
    DatHeapLocked--;
    return(ret);
    }

char *_d_cgets(char *s)
/*
Read a string of characters directly from the console.
*/
    {
    int i,c,maxLen,ql;
    char *p,*q;
    static char _b1[] = {'\b',' ','\b'};
    static char _b2[] = {'\b','\b',' ',' ','\b','\b'};
    static char _cr[] = {'\r'};

    maxLen=(s[0]&0xff);
    p=(&s[2]);
    i=0;
    while ((c=getch())!='\r')
        {
        if (c=='\b')
            {
	    if (i)
		{
        	i--;
		if (p[i]<' ')
		    {
		    q=(&_b2[0]);
		    ql=sizeof(_b2);
		    }
		else
		    {
		    q=(&_b1[0]);
		    ql=sizeof(_b1);
		    }
		_iow4(winHandle,P_FWRITE,q,&ql);
		}
            }
        else if (i<maxLen)
            {
	    if (c<' ')
		{
		putch('^');
		putch(c+'@');
		}
	    else
		putch(c);
            p[i++]=c;
            }
	else
	    putch(0x7);
        }
    ql=1;
    _iow4(winHandle,P_FWRITE,&_cr,&ql);
    s[1]=i;
    p[i]=0;
    return((char *)p);
    }

char _pname[P_FNAMESIZE];
P_FPARSE _pinfo;

int _isloc(const char *path)
/*
Returns true if path is on the local file system.
*/
    {

    if (_parse(path,"",&_pname[0],&_pinfo)<0)
	return(0);
    return(memcmp("LOC",&_pname[0],3)==0 ? 1 : 0);
    }

#define MAXD 4
typedef struct
    {
    int magic;
    char *handle;
    } _DCTL;

typedef struct
    {
    char *handle;
    int ctl;
    int magic;
    int attrib;
    int first;
    } _DRES;

_STATIC _DCTL _dctl[MAXD];
_STATIC int _dNext;

int findfirst(const char *path,struct ffblk *b,int attrib)
/*
Find the first matching directory entry.
*/
{
    int ret;
    char *h;
    _DRES *r;

    if (!_isloc(path))
	{
	errno=EINVAL;
	return(-1);
	}
    if ((h=_dctl[_dNext].handle)!=NULL)
	{
	_iow2(h,P_FCLOSE);
	_dctl[_dNext].handle=NULL;
	}
    _dctl[_dNext].magic++;
    if ((ret=_iopen(&h,(char *)path,P_FDIR))<0)
	{
failed:
	_ioerr(ret);
	return(-1);
	}
    r=(_DRES *)&b->ff_reserved[0];
    r->handle=h;
    r->ctl=_dNext;
    r->magic=_dctl[_dNext].magic;
    r->attrib=(attrib&0xff);
    r->first=ENOENT;
    _dctl[_dNext++].handle=h;
    if (_dNext>=MAXD)
	_dNext=0;
    return(findnext(b));
}

int findnext(struct ffblk *b)
/*
Find the next directory entry.
*/
    {
    int a;
    _DRES *r;
    P_INFO f;

    r=(_DRES *)&b->ff_reserved[0];
    if (_dctl[r->ctl].magic!=r->magic)
	{
failed:
	errno=(r->first ? ENOENT : ENMFILE);
	return(-1);
	}
    do
	{
	if ((a=_iow4(r->handle,P_FREAD,&b->ff_name[0],&f))<0)
	    {
	    _iow2(r->handle,P_FCLOSE);
	    _dctl[r->ctl].handle=0;
	    _dctl[r->ctl].magic++;
	    _ioerr(a);
	    return(-1);
	    }
	a=f.status&(FA_HIDDEN|FA_SYSTEM|FA_LABEL|FA_DIREC);
	} while (a && (a&r->attrib)==0);
    b->ff_attrib=(f.status&0xff)^1;
    b->ff_fsize=f.size;
    _convTimeToDos(f.modst,&b->ff_fdate,&b->ff_ftime);
    return(0);
    }

int utime(char *path,struct utm *ftime)
/*
Set the modification time of a file.
*/
    {
    INT ret;
    P_DATE dt;
    P_DAYSEC ds;
    time_t t;

    if (ftime)
	{
	dt.year=ftime->u_year+80;
	dt.month=ftime->u_mon-1;
	dt.day=ftime->u_day-1;
	dt.hour=ftime->u_hour;
	dt.minute=ftime->u_min;
	dt.second=ftime->u_sec;
	_dateToDaySeconds(&dt,&ds);
	t=_daySecondsToSystemTime(&ds);
	}
    else
	t=_date();
    if ((ret=_fileSetModTime(path,t))<0)
	{
	_ioerr(ret);
	return(-1);
	}
    return(0);
    }

int _filetime(int handle,struct utm *ftime)
/*
Get the modified time of the file open on handle.
*/
    {
    int ret;
    char name[P_FNAMESIZE];
    P_DAYSEC ds;
    P_DATE dt;
    P_INFO pI;

    _FsChanToName(getRealHandle(handle),&name[0]);
    if (name[0])
	{
	if ((ret=_fileInfo(&name[0],&pI))<0)
	    {
	    _ioerr(ret);
	    return(-1);
	    }
	_systemTimeToDaySeconds((time_t)pI.modst,&ds);
	_daySecondsToDate(&ds,&dt);
	if (dt.year<80)
	    goto baseDate;
	}
    else
	{
baseDate:
	memset(&dt,0,sizeof(dt));
	dt.year=80;
	}
    ftime->u_year=dt.year-80;
    ftime->u_mon=dt.month+1;
    ftime->u_day=dt.day+1;
    ftime->u_hour=dt.hour;
    ftime->u_min=dt.minute;
    ftime->u_sec=dt.second;
    return(0);
    }

int _filedrive(int handle)
/*
Return the drive letter if on LOC:: file system.
*/
    {
    char name[P_FNAMESIZE];

    _FsChanToName(getRealHandle(handle),&name[0]);
    if (name[0] && memcmp("LOC",&_pname[0],3)==0)
	return(name[5]-'A');
    return(0);
    }

int _execc(const char *path, char **argv, int mode)
/*
Generalised exec call.
*/
    {
    int pid;
    WORD stat;
    char *p,*pe,*s,name[P_FNAMESIZE],command[0x80];

    if (mode&_E_SEARCH)
	{
	mode&=(~_E_SEARCH);
	if (_parse(path,".img",&_pname[0],&_pinfo)<0)
	    {
	    errno=EINVAL;
	    return(-1);
	    }
	_searchenv(&_pname[_pinfo.system+_pinfo.device+_pinfo.path],"PATH",&name[0]);
	if (name[0]==0)
	    {
	    errno=ENOENT;
	    return(-1);
	    }
	}
    else
	strcpy(&name[0],path);
    argv++;
    p=(&command[0]);
    pe=p+0x7f;
    while (*argv)
	{
	*p++=' ';
	s=(*argv++);
	while (p<pe && *s)
	    *p++=(*s++);
	if (p>=pe)
	    {
	    errno=E2BIG;
	    return(-1);
	    }
	}
    *p++=0;
    command[0]=p-(&command[1]);
    if ((pid=__execc(&name[0],&command[0]))<0)
	{
	_ioerr(pid);
	return(-1);
	}
    if (mode&_E_EXEC || mode==P_OVERLAY)
	{
	_resume(pid);
	exit(0);
	}
    if (mode==P_NOWAIT)
	{
	_resume(pid);
	return(pid);
	}
    _logon(pid,&stat);
    _resume(pid);
    _waitStat(&stat);
    return(stat);
    }

int getdisk(void)
/*
Get the current disk.
*/
    {

    _getPath(&_pname[0]);
    if (!_isloc(&_pname[0]))
	return(0);
    return(_pname[5]-'A');
    }

int setdisk(int drive)
/*
Set the current disk.
*/
    {
    void *h;

    _getPath(&_pname[0]);
    if (!_isloc(&_pname[0]))
	return(0);
    _pname[5]='A'+drive;
    _setPath(&_pname[0]);
    _iopen(&h,"LOC::",P_FDEVICE);
    drive=0;
    while (_iow3(h,P_FREAD,&_pname[0])>=0)
	drive++;
    _iow2(h,P_FCLOSE);
    return(drive);
    }
	
int getftime(int handle,struct ftime *f)
/*
Get the file time from and open file.
*/
    {
    int ret;
    struct utm ftime;

    if ((ret=_filetime(handle, &ftime))<0)
	return(ret);
    f->ft_tsec = (unsigned) (ftime.u_sec>>1);
    f->ft_min = (unsigned) ftime.u_min;
    f->ft_hour= (unsigned) ftime.u_hour;
    f->ft_day= (unsigned) ftime.u_day;
    f->ft_month= (unsigned) ftime.u_mon;
    f->ft_year=ftime.u_year+1980;
    return(0);
    }

long _lseek(void *handle, long offset, int where)
/*
Seek on a file by handle.
*/
    {
    int ret,mode;
    long endPos,newPos;

    switch (where)
	{
	case SEEK_CUR:
	    mode=P_FCUR;
	    goto doSeek;
	case SEEK_END:
	    mode=P_FEND;
doSeek:
	    newPos=0l;
	    _iow4(handle,P_FSEEK,&mode,&newPos);
	    newPos+=offset;
	    break;
	case SEEK_SET:
	    newPos=offset;
	}
    if (newPos<0)
	newPos=0;
    endPos=newPos;
    mode=P_FABS;
    if ((ret=_iow4(handle,P_FSEEK,&mode,&newPos))<0)
	goto failed;
    if (newPos!=endPos)
	{
	newPos=endPos;
	if (_FsChanToMode(handle)&P_FUPDATE)
	    {
	    if ((ret=_iow3(handle,P_FSETEOF,&newPos))<0)
		goto failed;
	    if ((ret=_iow4(handle,P_FSEEK,&mode,&newPos))<0)
		{
failed:
		_ioerr(ret);
		return(-1);
		}
	    }
	}
    return(newPos);
    }

void gotoxy(int x, int y)
/*
Position the consol channel.
*/
    {
    UWORD mode;
    P_POINT p;

    p.x=x-1;
    p.y=y-1;
    mode=P_SCR_POSA;
    _iow4(winHandle,P_FSET,&mode,&p);
    }

int wherex(void)
/*
Return the X cursor position.
*/
    {
    P_RECTP rp;

    _iow3(winHandle,P_FSENSE,&rp);
    return(rp.p.x+1);
    }

int wherey(void)
/*
Return the Y cursor position.
*/
    {
    P_RECTP rp;

    _iow3(winHandle,P_FSENSE,&rp);
    return(rp.p.y+1);
    }

void clrscr(void)
/*
Clear the screen.
*/
    {
    UWORD mode;
    P_RECTP rp;

    _iow3(winHandle,P_FSENSE,&rp);
    mode=P_SCR_CLR;
    _iow4(winHandle,P_FSET,&mode,&rp.r);
    gotoxy(1,1);
    }

void cursoron(void)
/*
Switch the cursor on.
*/
    {
    UWORD mode,flag;

    flag=TRUE;
    mode=P_SCR_CURSOR;
    _iow4(winHandle,P_FSET,&mode,&flag);
    }

void cursoroff(void)
/*
Switch the cursor off.
*/
    {
    UWORD mode,flag;

    flag=FALSE;
    mode=P_SCR_CURSOR;
    _iow4(winHandle,P_FSET,&mode,&flag);
    }

void setwrap(int flag)
/*
Switch the wrap mode.
*/
    {
    UWORD mode;

    flag=(flag ? 1 : 0);
    mode=P_SCR_WLOCK;
    _iow4(winHandle,P_FSET,&mode,&flag);
    }

static void xLine(int flag)
/*
Insert or delete the line containg the cursor.
*/
    {
    UWORD mode;
    P_RECTP rp;

    _iow3(winHandle,P_FSENSE,&rp);
    rp.r.tl.x=0;
    rp.r.tl.y=rp.p.y;
    rp.p.x=0;
    rp.p.y=flag;
    mode=P_SCR_SCROLL;
    _iow4(winHandle,P_FSET,&mode,&rp);
    }

void insline(void)
/*
Insert a line.
*/
    {

    xLine(1);
    }

void delline(void)
/*
Delete a line.
*/
    {

    xLine(-1);
    }

void clreol(void)
/*
Clear to the end of line.
*/
    {
    UWORD mode;
    P_RECTP rp;

    _iow3(winHandle,P_FSENSE,&rp);
    rp.r.tl=rp.p;
    rp.r.br.y=rp.p.y+1;
    mode=P_SCR_CLR;
    _iow4(winHandle,P_FSET,&mode,&rp.r);
    }

